mysql之隔离级别(isolation level)

一、ACID:事务的4个特性

  • 原子性(atomicity):在一个事务中,操作要么成功,要么失败。如果事务中有一个命令失败,必须回滚操作,回到事务执行之前的状态。
  • 一致性(consistency):事务在完成时,必须使所有的数据都保持一致状态。
  • 隔离性(isolation):事务在查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务是不会查看中间状态的。
  • 持久性(durability):事务完成之后,它对于系统的影响是永久性的。

二、隔离级别(ACID中的I)

InnoDB提供四种隔离级别
  • READ UNCOMMITTED: 读未提交
  • READ COMMITTED: 读已提交
  • REPEATABLE READ: 重复读
  • SERIALIZABLE: 串行化

不一致情况: 脏读:一个事务读取了另一个未提交事务写入的数据。 不可重复读:指一个事务重新读取前面读取过的数据时,发现该数据已经被另一个已提交事务修改了。 幻读:一个事务开始后,需要根据数据库中现有的数据做一些更新,于是重新执行一个查询,返回一套符合查询条件的行,这是发现这些行因为其他最近提交的事务而发生了改变,导致现有的事务如果再进行下去就可能会在逻辑上出现一些错误。(幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。)

事务隔离级别的行为
隔离级别 脏读 不可重复读 幻读
读未提交 可能 可能 可能
读已提交 不可能 可能 可能
重复读 不可能 不可能 可能
可串行化 不可能 不可能 不可能

模拟发生幻读:

事务1

事务2

事务1:检查表中是否有id=10的记录,没有则插入,这是正常逻辑。 事务2: 干拢了事务1的正常执行。


用户可以通过SET TRANSACTION命令改变隔离级别,或通过--transaction-isolation配置server级别的隔离性。 MySQL通过使用不同的锁策略实现不同的隔离级别,默认隔离级别是REPEATABLE READ,以提供更高级别的一致性。你也可以指定隔离级别为READ COMMITTED甚至READ UNCOMMITTED一致性要求没那么高的场景(减少锁以提高性能)。SERIALIZABLE提供比REPEATABLE更严格的 规则,一般会用于一些特殊的场景,比如XA事务和用于调试并发或死锁问题。


REPEATABLE READ

这是MySQL的默认隔离级别。在同一事务中的读取的数据是一致的,在第一次读时会建立一个快照,同一事务中读取到的都是第一个快照中的值。也就是说在同一个事务中,SELECT(noblocking)所获取到的数据都是一致的。对于REPEATABLE READ隔离级别总是读取事务开始时的行数据。


READ COMMITTED 读已提交

每一次读都会建立一个新的快照并从中读取数据,它总是读取行的最新版本,如果行被锁定,则读取该行版本的最新一个快照。 对于READ COMMITTED只支持 row-based的binlog。 其它影响: - 对于UPDATE,DELETE操作,InnoDB只会锁住需要需要更新或删除的行。对于不匹配的行,在执行完WHERE语句后锁会释放。这极大的降低了死锁的概率,但还是有可能发生。 - 对于UPDATE操作,如果行已经被加锁,InnoDB``表现为semi-consistent读,将返回最近提交的版本,MySQL再决定是否符合WHERE条件。如果匹配,MySQL```会重新读取这行,然后获取锁或者等待锁。


READ UNCOMMITTED

SELECT表现为nonlocking fashion,但是可能会读取到旧版本的数据,所以这种隔离级别不保持一致性读,也称为脏读。


SERIALIZABLE

SERIALIZABLEREPEATABLE READ隔离级别类似,当autocommit=false情况下,所有SELECT语句会被转换为SELECT ... LOCK IN SHARE MODE


示例: MySQL默认隔离级别为: REPEATABLE READ隔离级别情况下,开启两个session,先左边的窗口开启一个事务读取表a中的数据,然后右边的窗口再开启个事务对表a数据进行修改,右边的事务提交后,在左边的事务再次查看表a中的数据。发现两次得到的数据是一致性的,因为对于REPEATABLE READ隔离级别总是读取事务开始时的行数据。 READ COMMITTED隔离级别重复上述的操作,发现在右边的事务提交后再查看表a的数据,此时表a的数据已更新。因为在这个隔离级别下,它总是读取行的最新版本,如果行被锁定,则读取该行版本的最新一个快照。


Ref:
1.innodb-transaction-isolation-levels